Laravel 配置项即时载入的服务提供者 (根目录:/var/www/laravel/)

所有即时载入的服务注册完成之后,会立即调用 register 方法,并标记为已载入。
随后通过 \Illuminate\Foundation\Bootstrap\BootProviders 启动项来调用所有的即时加载服务提供者的 boot 方法

查看根据 config/app.php 中的 providers 生成的 bootstrap/cache/services.php
'eager' => 
  array (
    // 系统服务提供者
    0 => 'Illuminate\\Auth\\AuthServiceProvider',                       // 注入验证相关对象
    1 => 'Illuminate\\Cookie\\CookieServiceProvider',                   // 注入cookie对象
    2 => 'Illuminate\\Database\\DatabaseServiceProvider',               // 注入db相关对象
    3 => 'Illuminate\\Encryption\\EncryptionServiceProvider',           // 注入加解密对象
    4 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',           // 注入文件相关对象
    5 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',// 注入基础的请求对象
    6 => 'Illuminate\\Notifications\\NotificationServiceProvider',      // 注入通知对象
    7 => 'Illuminate\\Pagination\\PaginationServiceProvider',           // 注入分页相关对象
    8 => 'Illuminate\\Session\\SessionServiceProvider',                 // 注入session相关对象
    9 => 'Illuminate\\View\\ViewServiceProvider',                       // 注入视图相关对象
    10 => 'Laravel\\Passport\\PassportServiceProvider',                 // 注入passport相关对象
    
    // 配置项服务提供者
    11 => 'App\\Providers\\AppServiceProvider',
    12 => 'App\\Providers\\AuthServiceProvider',
    13 => 'App\\Providers\\EventServiceProvider',
    14 => 'App\\Providers\\RouteServiceProvider',
  )

路由相关服务提供者

// 主要是注册完之后的 boot 方法调用
App\\Providers\\RouteServiceProvider
public function boot()
{
    // 可以加入自己的操作
    parent::boot();
}
public function boot()
{
    $this->setRootControllerNamespace();
    // 若执行了 php artisan routes:cache(自动生成路由缓存文件,注意:建议只在项目上线时操作),直接加载 
    if ($this->app->routesAreCached()) {
        $this->loadCachedRoutes();
    } else {
        // 加载路由
        $this->loadRoutes();
        // 设置系统启动时的事件监听函数
        $this->app->booted(function () {
            $this->app['router']->getRoutes()->refreshNameLookups();
        });
    }
}
protected function setRootControllerNamespace()
{
    if (! is_null($this->namespace)) {
        // 设置 $this->instances['url'] (\Illuminate\Routing\UrlGenerator对象)的 rootNamespace 属性
        $this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
    }
}
public function routesAreCached()
{
    return $this['files']->exists($this->getCachedRoutesPath());
}
public function getCachedRoutesPath()
{
    return $this->bootstrapPath().'/cache/routes.php';
}
protected function loadRoutes()
{
    if (method_exists($this, 'map')) {
        $this->app->call([$this, 'map']);
    }
}
public function map()
{
    $this->mapApiRoutes();
    $this->mapWebRoutes();
}
// API 相关的路由
protected function mapApiRoutes()
{
    Route::prefix('api')
         ->middleware('api')
         ->namespace($this->namespace)
         ->group(base_path('routes/api.php'));
}
// WEB 相关的路由
protected function mapWebRoutes()
{
    Route::middleware('web')
         ->namespace($this->namespace)
         ->group(base_path('routes/web.php'));
}
public function booted($callback)
{
    $this->bootedCallbacks[] = $callback;
    // 如果应用已经启动了,则直接调用
    if ($this->isBooted()) {
        $this->fireAppCallbacks([$callback]);
    }
}

// Route 是 Facde 的调用方式
分析: Route::middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/web.php'));

Route::middleware
public static function __callStatic($method, $args)
{
    // 获取应用的 router 对象
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }
    // 将 Router::middleware() 转化为应用的 Router->middleware() 方式,若 Router 没有 middleware 方法,则直接触发 __call
    return $instance->$method(...$args);
}
public function __call($method, $parameters)
{
    if (static::hasMacro($method)) {
        return $this->macroCall($method, $parameters);
    }
    // 将不存在的方法的调用委托给 RouteRegistrar 处理,
    return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
}
public function attribute($key, $value)
{
    // 只允许指定的属性设置($allowedAttributes = ['as', 'domain', 'middleware', 'name', 'namespace', 'prefix',])
    if (! in_array($key, $this->allowedAttributes)) {
        throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
    }
    // 支持.方式来存放属性
    $this->attributes[array_get($this->aliases, $key, $key)] = $value;

    return $this;
}
public function group($callback)
{
    $this->router->group($this->attributes, $callback);
}
public function group(array $attributes, $routes)
{
    $this->updateGroupStack($attributes);
    $this->loadRoutes($routes);
    array_pop($this->groupStack);
}
protected function loadRoutes($routes)
{
    if ($routes instanceof Closure) {
        $routes($this);
    } else {
        $router = $this;
        require $routes;
    }
}

小结

Route::middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/web.php'));
        
实际就是将 middleware namespace 加入到 RouteRegistrar 对象里面的 attributes 数组属性,并返回
RouteRegistrar 对象,再调用 RouteRegistrar 对象的 group 方法,也就是调用 router 对象的 group
方法。最终就是将['middleware' => 'web', 'namespace' => $this->namespace] 数组设置给 
$this->router 的groupStack 属性(将运用到所有的路由),再加载 config/web.php。

TylerZou
70 声望20 粉丝

I have a dream!